﻿// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.

using System;
using PortAudioSharp;

namespace Byond.Standalone.PortAudioStuff
{
	public class PortAudioWrapper : IDisposable
	{
		private readonly uint _framesPerBuffer;
		private readonly int _frequency;
		private readonly PortAudio.PaStreamCallbackDelegate _paStreamCallback;
		private readonly PortAudio.PaDeviceInfo _inputDeviceInfo;
		private readonly PortAudio.PaDeviceInfo _outputDeviceInfo;

		private bool _disposed;
		private IntPtr _stream;

		public PortAudioWrapper(
			int frequency,
			uint framesPerBuffer,
			PortAudio.PaDeviceInfo inputDevice,
			PortAudio.PaDeviceInfo outputDevice,
			PortAudio.PaStreamCallbackDelegate paStreamCallback)
		{
			_frequency = frequency;
			_framesPerBuffer = framesPerBuffer;
			_paStreamCallback = paStreamCallback;
			_inputDeviceInfo = inputDevice;
			_outputDeviceInfo = outputDevice;
		}

		public void Dispose()
		{
			if (!_disposed)
				PortAudioExtensions.ErrorCheck(PortAudio.Pa_Terminate());
			_disposed = true;
			GC.SuppressFinalize(this);
		}

		~PortAudioWrapper()
		{
			Dispose();
		}

		public void Start()
		{
			PortAudioExtensions.Initialize();
			_stream = StreamOpen();
			StreamStart(_stream);
		}

		public void Stop()
		{
			StreamStop(_stream);
			StreamClose(_stream);
			_stream = new IntPtr(0);
		}

		private IntPtr StreamOpen()
		{
			var stream = new IntPtr();
			var userData = new IntPtr(0);
			var inputParameters = _inputDeviceInfo.Equals(PortAudioExtensions.NoDevice)
				? null
				: new PortAudio.PaStreamParameters
					{
						channelCount = _inputDeviceInfo.maxInputChannels,
						device = _inputDeviceInfo.DeviceNumber(),
						sampleFormat = PortAudio.PaSampleFormat.paFloat32,
						suggestedLatency = _inputDeviceInfo.defaultLowInputLatency
					};
			var outputParameters = _outputDeviceInfo.Equals(PortAudioExtensions.NoDevice)
				? null
				: new PortAudio.PaStreamParameters
					{
						channelCount = _outputDeviceInfo.maxOutputChannels,
						device = _outputDeviceInfo.DeviceNumber(),
						sampleFormat = PortAudio.PaSampleFormat.paFloat32,
						suggestedLatency = _outputDeviceInfo.defaultLowOutputLatency
					};
			
			PortAudioExtensions.ErrorCheck(
				PortAudio.Pa_OpenStream(
					out stream,
					inputParameters,
					outputParameters,
					_frequency,
					_framesPerBuffer,
					PortAudio.PaStreamFlags.paNoFlag,
					_paStreamCallback,
					userData));
			
			return stream;
		}

		private static void StreamClose(IntPtr stream)
		{
			PortAudioExtensions.ErrorCheck(PortAudio.Pa_CloseStream(stream));
		}

		private static void StreamStart(IntPtr stream)
		{
			PortAudioExtensions.ErrorCheck(PortAudio.Pa_StartStream(stream));
		}

		private static void StreamStop(IntPtr stream)
		{
			PortAudioExtensions.ErrorCheck(PortAudio.Pa_StopStream(stream));
		}
	}
}